#region Using References

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using Ionic.Zip;

#endregion

namespace Beaker.Services.FileSystem.Zip
{
	public class ZipFileSystemService
		: IFileSystemService, IFile
	{
		private readonly IFolder _parent;
		private readonly IFile _file;

		private ZipFile _zipFile;
		private ZipFolderEntry _root;

		public ZipFileSystemService( IFile file )
		{
			Contract.Requires<ArgumentNullException>( file != null );

			_file = file;
			_parent = _file.ParentFolder;

			_root = new ZipFolderEntry( this, _parent, file.Name );
		}

		private static readonly char[] FolderSeperatorChars =
			new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };

		private ZipFile OpenZipFile()
		{
			if( _zipFile == null )
			{
				_zipFile = ZipFile.Read( _file.OpenRead() );

				foreach( ZipEntry entry in _zipFile.Entries )
				{
					ZipFolderEntry folder = _root;
					string[] segments = entry.FileName.Split( FolderSeperatorChars );

					for( int i = 0; i < segments.Length - 1; i++ )
						folder = (ZipFolderEntry)folder.GetOrCreateFolder( segments[i] );

					folder.GetOrCreateFile( entry, segments[segments.Length - 1] );
				}
			}

			return _zipFile;
		}

		private void IndexEntry( ZipEntry entry )
		{
			string[] folders = entry.FileName.Split( new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar } );
		}

		/// <summary>
		/// Gets the name of the zip file
		/// </summary>
		public string Name
		{
			get { return _file.Name; }
		}

		/// <summary>
		/// Get the folder this zip file is in
		/// </summary>
		public IFolder ParentFolder
		{
			get { return _parent; }
		}

		/// <summary>
		/// Gets the files in this folder.
		/// </summary>
		/// <returns>
		/// A sequence of files.
		/// </returns>
		public IEnumerable<IFile> GetFiles()
		{
			OpenZipFile();
			return _root.GetFiles();
		}

		/// <summary>
		/// Gets the file with the given name.
		/// </summary>
		/// <param name="fileName">The name of the file to get.</param>
		/// <returns>The file with the given name, or <c>null</c> when it could not be found.</returns>
		public IFile GetFile( string fileName )
		{
			OpenZipFile();
			return _root.GetFile( fileName );
		}

		/// <summary>
		/// Gets the folders in this folder.
		/// </summary>
		/// <returns>
		/// A sequence of folders.
		/// </returns>
		public IEnumerable<IFolder> GetFolders()
		{
			OpenZipFile();
			return _root.GetFolders();
		}

		/// <summary>
		/// Gets the folder with the given name.
		/// </summary>
		/// <param name="folderName">The name of the folder to get.</param>
		/// <returns>The folder with the given name, or <c>null</c> when it could not be found.</returns>
		public IFolder GetFolder( string folderName )
		{
			OpenZipFile();
			return _root.GetFolder( folderName );
		}

		/// <summary>
		/// Gets the entires in this folder.
		/// </summary>
		/// <returns>
		/// A sequence of items.
		/// </returns>
		public IEnumerable<IFileSystemEntry> GetEntries()
		{
			OpenZipFile();
			return _root.GetEntries();
		}

		/// <summary>
		/// Gets the entry with the given name in this folder.
		/// </summary>
		/// <param name="entryName">Name of the entry to get.</param>
		/// <returns>The entry with the given name, or <c>null</c> when it could not be found.</returns>
		public IFileSystemEntry GetEntry( string entryName )
		{
			OpenZipFile();
			return _root.GetEntry( entryName );
		}

		/// <summary>
		/// Parse the given path and return a file or folder for it
		/// </summary>
		/// <param name="path">The path to parse</param>
		/// <returns>A IFileSystemEntry with the file or folder, or <c>null</c> when the file or folder does not exist</returns>
		/// <exception cref="FormatException">Thrown when the path could not be parsed.</exception>
		public IFileSystemEntry ParsePath( string path )
		{
			OpenZipFile();
			throw new NotImplementedException();
		}

		/// <summary>
		/// Gets the requested special folder
		/// </summary>
		/// <param name="folder">The special folder to get</param>
		/// <returns>The requested special folder</returns>
		public IFolder GetSpecialFolder( Environment.SpecialFolder folder )
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Open the file for reading
		/// </summary>
		/// <returns>A stream of the content of the file</returns>
		public Stream OpenRead()
		{
			return _file.OpenRead();
		}

		/// <summary>
		/// Open the text-file for reading
		/// </summary>
		/// <returns>A text-reader with the content of the file</returns>
		public TextReader OpenText()
		{
			return _file.OpenText();
		}
	}
}